/***********************************************
 This file is part of redia-bapps-main-ios.
 
 Copyright (c) 2015 Redia A/S
 
 Redia-bapps-main-ios is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 
 Redia-bapps-main-ios is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with redia-bapps-main-ios.  If not, see <http://www.gnu.org/licenses/>.
 
 *********************************************** */

//
//  BappsManager.m
//  bapps
//
//  Created by Nikolaj Tørring on 8/1/13.
//  Copyright (c) 2013 redia. All rights reserved.
//

#import "BARPCManager.h"

#include <sys/socket.h>
#include <netdb.h>

static NSMutableDictionary *connectionsForViews = nil;
static NSMutableArray *connectionsOrphaned = nil;

@interface BARPCManager ()

@property (nonatomic, strong) NSString* libraryApiKey; //!< An API Key associated with the specified libraryId

@property (nonatomic, strong) NSString* libraryId;
@property (assign, nonatomic) NSTimeInterval sessionTimestamp; //!< This timestamp keeps track of the last time an authenticated action was performed.

@property (nonatomic, strong) NSArray* developerServers;
@property (nonatomic, strong) NSArray* productionServers;
@end

@implementation BARPCManager

+(void)initialize
{
    connectionsForViews = [NSMutableDictionary new];
    connectionsOrphaned = [NSMutableArray new];
}

SINGLETON_IMPL_GCD(BARPCManager)

-(id)init{
    self = [super init];
    if (self) {
        _language = BARPCMANAGER_DEFAULT_LANGUAGE;
        _libraryApiKey = BA_RPC_MANAGER_LIBRARY_API_KEY;
        
        self.developerServers = BA_RPC_DEVELOPER_SERVERS;
        
        self.productionServers = BA_RPC_PRODUCTION_SERVERS;
        
    }
    return self;
}


-(void)setAndConfigureLibraryId:(NSString*)libraryId;
{
    self.libraryId = libraryId;
}

+(void)closeConnectionsForView:(id)view{
    if (view == nil) {
        return;
    }
    
    NSValue* view_as_key = [NSValue valueWithPointer:(__bridge const void *)(view)];
    NSMutableArray* connectionsArray = [connectionsForViews objectForKey:view_as_key];
    for (JSONRPCClient* client in connectionsArray) {
        [client stopRequests];
    }
    [connectionsForViews removeObjectForKey:view_as_key];

    /* was:
    for (NSValue* key in [connectionsForViews allKeysForObject:view]) {
        JSONRPCClient* client = (JSONRPCClient*) [key pointerValue];
        [client stopRequests];
        DLog(@"canceled request for client %@",client)
        [connectionsForViews removeObjectForKey:key];
        //[connectionsLookup removeObjectForKey:key];
    }
    */
    
}

+(void)addConnection:(JSONRPCClient*)connection View:(id)view{
    if (connection == nil) {
        [NSException raise:@"Adding nil connections not allowed" format:@"Can't add nil connections to BARPCManager"];
        return;
    }
    
    if (view==nil) {
        [connectionsOrphaned addObject:connection];
        
    } else {
    
        NSValue* view_as_key = [NSValue valueWithPointer:(__bridge const void *)(view)];
        NSMutableArray* connectionsArray = [connectionsForViews objectForKey:view_as_key];
        if (connectionsArray!=nil) {
            [connectionsArray addObject:connection];
        } else {
            [connectionsForViews setObject:[[NSMutableArray alloc] initWithObjects:connection, nil] forKey:view_as_key];
        }
    }
    /* was:
    NSValue* key = [NSValue valueWithPointer:(__bridge const void *)(connection)];
    [connectionsForViews setObject:view forKey:key];
    //[connectionsLookup setObject:connection forKey:key];
     */
}

+(void)removeConnection:(JSONRPCClient*)connection {
    if (connection == nil) {
        [NSException raise:@"Removing nil connection not allowed" format:@"Can't remove nil connection to BARPCManager"];
        return;
    }

    [connectionsOrphaned removeObject:connection];
    
    NSMutableArray* keysToBeRemoved = [NSMutableArray new];
    
    for (NSValue* view_as_key in [connectionsForViews allKeys]) {
        NSMutableArray* connectionsArray = [connectionsForViews objectForKey:view_as_key];
        [connectionsArray removeObject:connection];
        if ([connectionsArray count]==0) {
            [keysToBeRemoved addObject:view_as_key];
        }
    }

    for (NSValue* view_as_key in keysToBeRemoved) {
        [connectionsForViews removeObjectForKey:view_as_key];
    }
    
    //debug
    /*
    int num=[connectionsOrphaned count];
    for (NSValue* view_as_key in [connectionsForViews allKeys]) {
        NSMutableArray* connectionsArray = [connectionsForViews objectForKey:view_as_key];
        for (id obj in connectionsArray) {
            num++;
        }
    }
    DLog(@"now connections: %d",num);
    */
    
    /* was:
    NSValue* key = [NSValue valueWithPointer:(__bridge const void *)(connection)];
    [connectionsForViews removeObjectForKey:key];
    //[connectionsLookup removeObjectForKey:key];
    */
}

+(void)isSupportedAsyncFromView:(id)view
                         device:(NSString*)device
                         appName:(NSString*)appName
                         version:(NSString*)version
                         osVersion:(NSString*)osVersion
               resultBlock:(void(^)(BASupportInfo* result))resultBlock
                errorBlock:(void(^)(int errorCode, NSString* errorMessage))errorBlock
{
    BARPCManager* manager = [BARPCManager instance];
    NSArray *params = @[manager.libraryApiKey, manager.language, device, appName, version, osVersion];
    [self performRPCMethod:@"library.isSupported" args:params requestId:BA_REQID_IS_SUPPORTED view:view resultBlock:resultBlock errorBlock:errorBlock];
}

+(void)searchAsyncFromView:(id)view
                     query:(NSString*)query
                    filter:(NSDictionary*)filters
                     count:(NSUInteger)count
                    offset:(NSUInteger)offset
               resultBlock:(void(^)(BASearchResult* result))resultBlock
                errorBlock:(void(^)(int errorCode, NSString* errorMessage))errorBlock
{
    BARPCManager* manager = [BARPCManager instance];
    NSArray *params = @[manager.libraryApiKey, manager.libraryId, manager.language, query, [NSNumber numberWithInt:offset], [NSNumber numberWithInt:count], filters];
    [self performRPCMethod:@"library.search" args:params requestId:BA_REQID_SEARCH view:view resultBlock:resultBlock errorBlock:errorBlock];
}



+(void)getObjectsAsyncFromView:(id)view
                           ids:(NSArray*)ids
            includeHoldingInfo:(BOOL)includeHoldingInfo
                   resultBlock:(void(^)(NSDictionary* result))resultBlock
                    errorBlock:(void(^)(int errorCode, NSString* errorMessage))errorBlock{
    BARPCManager* manager = [BARPCManager instance];
    NSArray *params = @[manager.libraryApiKey, manager.libraryId, manager.language, ids, [NSNumber numberWithBool:includeHoldingInfo]];
    [self performRPCMethod:@"library.getObjects" args:params requestId:BA_REQID_GET_OBJECTS view:view resultBlock:resultBlock errorBlock:errorBlock];
}



+(void)versionAsyncFromView:(id)view resultBlock:(void (^)(id))resultBlock errorBlock:(void (^)(int errorCode, NSString* errorMessage))errorBlock
{
    //BARPCManager* manager = [BARPCManager instance];
    NSArray *params = @[];
    [self performRPCMethod:@"library.version" args:params requestId:BA_REQID_VERSION view:view resultBlock:resultBlock errorBlock:errorBlock];

}

+(void)authenticateAsyncFromView:(id)view username:(NSString *)username password:(NSString *)password resultBlock:(void (^)(BAPatron* result))resultBlock errorBlock:(void (^)(int errorCode, NSString* errorMessage))errorBlock
{
    BARPCManager* manager = [BARPCManager instance];
    NSArray *params = @[manager.libraryApiKey, manager.libraryId, manager.language, username, password];
    [self performRPCMethod:@"library.authenticate" args:params requestId:BA_REQID_AUTHENTICATE view:view resultBlock:resultBlock errorBlock:errorBlock];

}

+(void)deauthenticateAsyncFromView:(id)view resultBlock:(void (^)(id))resultBlock errorBlock:(void (^)(int errorCode, NSString* errorMessage))errorBlock
{
    BARPCManager* manager = [BARPCManager instance];
    
    NSArray *params = @[manager.libraryApiKey, manager.libraryId, manager.language];
    [self performRPCMethod:@"library.deauthenticate" args:params requestId:BA_REQID_DEAUTHENTICATE view:view resultBlock:resultBlock errorBlock:errorBlock];
    
    
    NSHTTPCookieStorage* cs = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    for (NSString* cycleurl in [BARPCManager instance].serverUrlStrings) {
        NSURL* serverUrlUrl = [NSURL URLWithString:cycleurl];
        NSArray* cookies = [cs cookiesForURL:serverUrlUrl].copy;
        for (NSHTTPCookie* cookie in cookies) {
            [cs deleteCookie:cookie];
        }
    }
    manager.currentPHPSessionID = nil;
    manager.currentRouteIDCookie = nil;
    manager.cookiesDict = nil;
}

+(void)isAuthenticatedAsyncFromView:(id)view resultBlock:(void (^)(id))resultBlock errorBlock:(void (^)(int errorCode, NSString* errorMessage))errorBlock
{
    //BARPCManager* manager = [BARPCManager instance];
    NSArray *params = @[];
    [self performRPCMethod:@"library.isAuthenticated" args:params requestId:BA_REQID_IS_AUTHENTICATED view:view resultBlock:resultBlock errorBlock:errorBlock];

}


+(void)getBranchesAsyncFromView:(id)view resultBlock:(void (^)(NSArray* result))resultBlock errorBlock:(void (^)(int errorCode, NSString* errorMessage))errorBlock
{
    BARPCManager* manager = [BARPCManager instance];
    NSArray *params = @[manager.libraryApiKey, manager.libraryId, manager.language];
    [self performRPCMethod:@"library.getBranches" args:params requestId:BA_REQID_GET_BRANCHES view:view resultBlock:resultBlock errorBlock:errorBlock];
}

+(void)getMoreInSameSeriesAsyncFromView:(id)view identifier:(NSString *)identifier offset:(NSUInteger)offset count:(NSUInteger)count filters:(NSArray *)filters facets:(NSArray *)facets resultBlock:(void (^)(BASearchResult* result))resultBlock errorBlock:(void (^)(int errorCode, NSString* errorMessage))errorBlock
{
    BARPCManager* manager = [BARPCManager instance];
    NSArray *params = @[manager.libraryApiKey, manager.libraryId, manager.language, identifier, [NSNumber numberWithInt:offset], [NSNumber numberWithInt:count]];
    [self performRPCMethod:@"library.getMoreInSameSeries" args:params requestId:BA_REQID_GET_MORE_IN_SAME_SERIES view:view resultBlock:resultBlock errorBlock:errorBlock];

}

+(void)getRelatedLoansAsyncFromView:(id)view identifiers:(NSArray *)identifiers resultBlock:(void (^)(NSDictionary *result))resultBlock errorBlock:(void (^)(int errorCode, NSString* errorMessage))errorBlock
{
    BARPCManager* manager = [BARPCManager instance];
    NSArray *params = @[manager.libraryApiKey, manager.libraryId, manager.language, identifiers];
    [self performRPCMethod:@"library.getRelatedLoans" args:params requestId:BA_REQID_GET_RELATED_LOANS view:view resultBlock:resultBlock errorBlock:errorBlock];

}

+(void)getAboutCreatorAsyncFromView:(id)view creators:(NSArray *)creators resultBlock:(void (^)(NSDictionary *result))resultBlock errorBlock:(void (^)(int errorCode, NSString* errorMessage))errorBlock
{
    
    BARPCManager* manager = [BARPCManager instance];
    NSArray *params = @[manager.libraryApiKey, manager.libraryId, manager.language, creators];
    [self performRPCMethod:@"library.getAboutCreators" args:params requestId:BA_REQID_GET_ABOUT_CREATOR view:view resultBlock:resultBlock errorBlock:errorBlock];

}

+(void)getReviewsAsyncFromView:(id)view identifiers:(NSArray *)identifiers resultBlock:(void (^)(NSDictionary *result))resultBlock errorBlock:(void (^)(int errorCode, NSString* errorMessage))errorBlock
{
    BARPCManager* manager = [BARPCManager instance];
    NSArray *params = @[manager.libraryApiKey, manager.libraryId, manager.language, identifiers];
    [self performRPCMethod:@"library.getReviews" args:params requestId:BA_REQID_GET_REVIEWS view:view resultBlock:resultBlock errorBlock:errorBlock];

}

+(void)getCoverUrlsAsyncFromView:(id)view coverUrlQueries:(NSArray *)coverUrlQueries resultBlock:(void (^)(NSDictionary* result))resultBlock errorBlock:(void (^)(int errorCode, NSString* errorMessage))errorBlock
{
    BARPCManager* manager = [BARPCManager instance];
    NSArray *params = @[manager.libraryApiKey, manager.libraryId, manager.language, coverUrlQueries];
    [self performRPCMethod:@"library.getCoverUrls" args:params requestId:BA_REQID_GET_COVER_URLS view:view resultBlock:resultBlock errorBlock:errorBlock];

}

+(void)getReservationsAsyncFromView:(id)view resultBlock:(void (^)(NSArray* reservations))resultBlock errorBlock:(void (^)(int, NSString *))errorBlock
{
    BARPCManager* manager = [BARPCManager instance];
    NSArray *params = @[manager.libraryApiKey, manager.libraryId, manager.language];
    [self performRPCMethod:@"library.getReservations" args:params requestId:BA_REQID_GET_RESERVATIONS view:view resultBlock:resultBlock errorBlock:errorBlock];

    manager.reservationsLastUpdated = [[NSDate date] timeIntervalSince1970];
}

+(void)removeReservationsAsyncFromView:(id)view reservationIds:(NSArray *)reservationIds resultBlock:(void (^)(id))resultBlock errorBlock:(void (^)(int, NSString *))errorBlock
{
    BARPCManager* manager = [BARPCManager instance];
    NSArray *params = @[manager.libraryApiKey, manager.libraryId, manager.language, reservationIds];
    [self performRPCMethod:@"library.removeReservations" args:params requestId:BA_REQID_REMOVE_RESERVATIONS view:view resultBlock:resultBlock errorBlock:errorBlock];
    
}

+(void)addReservationAsyncFromView:(id)view reservationId:(NSString *)reservationId resultBlock:(void (^)(id))resultBlock errorBlock:(void (^)(int, NSString *))errorBlock
{
    BARPCManager* manager = [BARPCManager instance];
    NSArray *params = @[manager.libraryApiKey, manager.libraryId, manager.language, reservationId];
    [self performRPCMethod:@"library.addReservation" args:params requestId:BA_REQID_ADD_RESERVATION view:view resultBlock:resultBlock errorBlock:errorBlock];
    
}

+(void)getAvailabilityAsyncFromView:(id)view
                        identifiers:(NSArray *)identifiers
                 includeHoldingInfo:(BOOL)includeHoldingInfo
                        resultBlock:(void (^)(NSDictionary *))resultBlock
                         errorBlock:(void (^)(int, NSString *))errorBlock
{
    BARPCManager* manager = [BARPCManager instance];
    NSArray *params = @[manager.libraryApiKey, manager.libraryId, manager.language, identifiers, [NSNumber numberWithBool:includeHoldingInfo]];
    [self performRPCMethod:@"library.getAvailability" args:params requestId:BA_REQID_GET_AVAILABILITY view:view resultBlock:resultBlock errorBlock:errorBlock];

}


+(void)getLoansAsyncFromView:(id)view resultBlock:(void (^)(NSArray *))resultBlock errorBlock:(void (^)(int, NSString *))errorBlock
{
    BARPCManager* manager = [BARPCManager instance];
    NSArray *params = @[manager.libraryApiKey, manager.libraryId, manager.language];
    [self performRPCMethod:@"library.getLoans" args:params requestId:BA_REQID_GET_LOANS view:view resultBlock:resultBlock errorBlock:errorBlock];
    
}

+(void)renewLoansAsyncFromView:(id)view loanIds:(NSArray *)loanIds resultBlock:(void (^)(NSDictionary *))resultBlock errorBlock:(void (^)(int, NSString *))errorBlock
{
    BARPCManager* manager = [BARPCManager instance];
    NSArray *params = @[manager.libraryApiKey, manager.libraryId, manager.language, loanIds];
    [self performRPCMethod:@"library.renewLoans" args:params requestId:BA_REQID_RENEW_LOANS view:view resultBlock:resultBlock errorBlock:errorBlock];
    
}

+(void)renewAllLoansAsyncFromView:(id)view resultBlock:(void (^)(NSDictionary *))resultBlock errorBlock:(void (^)(int, NSString *))errorBlock
{
    BARPCManager* manager = [BARPCManager instance];
    NSArray *params = @[manager.libraryApiKey, manager.libraryId, manager.language];
    [self performRPCMethod:@"library.renewAllLoans" args:params requestId:BA_REQID_RENEW_ALL_LOANS view:view resultBlock:resultBlock errorBlock:errorBlock];
}

+(void)getOpeningHoursAsyncFromView:(id)view resultBlock:(void (^)(NSArray *))resultBlock errorBlock:(void (^)(int, NSString *))errorBlock
{
    BARPCManager* manager = [BARPCManager instance];
    NSArray *params = @[manager.libraryApiKey, manager.libraryId, manager.language];
    [self performRPCMethod:@"library.getOpeningHours" args:params requestId:BA_REQID_GET_OPENING_HOURS view:view resultBlock:resultBlock errorBlock:errorBlock];
    
}

+ (void)getMoreByCreatorFromView:(id)view
                      identifier:(NSString *)identifier
                          offset:(NSUInteger)offset
                           count:(NSUInteger)count
                         filters:(NSDictionary *)filters
                          facets:(NSArray *)facets
                     resultBlock:(void (^)(BASearchResult *))resultBlock
                      errorBlock:(void (^)(int, NSString *))errorBlock
{
    BARPCManager* manager = [BARPCManager instance];
    NSArray *params = @[manager.libraryApiKey, manager.libraryId, manager.language, identifier, [NSNumber numberWithUnsignedInteger:offset], [NSNumber numberWithUnsignedInteger:count], filters != nil ? filters : @{}, facets != nil ? facets : @[] ];
    [self performRPCMethod:@"library.getMoreByCreators" args:params requestId:BA_REQID_GET_MORE_BY_CREATOR view:view resultBlock:resultBlock errorBlock:errorBlock];
    
}

+(void)loadAllEventsFromView:(id)view resultBlock:(void (^)(NSArray *))resultBlock errorBlock:(void (^)(int, NSString *))errorBlock
{
    BARPCManager* manager = [BARPCManager instance];
    NSArray *params = @[manager.libraryApiKey, manager.libraryId, manager.language];
    //: TEST
//    NSArray *params = @[@"234jm8f", @"bob", manager.language];
//    NSArray *params = @[@"842jmdD", @"koldingbib", manager.language];
//    NSArray *params = @[@"002Hdk3", @"koebenhavnsbib", manager.language];
    [self performRPCMethod:@"library.loadAllEvents" args:params requestId:BA_REQID_LOAD_ALL_EVENTS view:view resultBlock:resultBlock errorBlock:errorBlock];

}

+(void)loadAllNewsFromView:(id)view resultBlock:(void (^)(NSArray *))resultBlock errorBlock:(void (^)(int, NSString *))errorBlock
{
    BARPCManager* manager = [BARPCManager instance];
    NSArray *params = @[manager.libraryApiKey, manager.libraryId, manager.language];
    //: TEST
//    NSArray *params = @[@"234jm8f", @"bob", manager.language];
//    NSArray *params = @[@"842jmdD", @"koldingbib", manager.language];
//    NSArray *params = @[@"002Hdk3", @"koebenhavnsbib", manager.language];
    [self performRPCMethod:@"library.loadAllNews" args:params requestId:BA_REQID_LOAD_ALL_NEWS view:view resultBlock:resultBlock errorBlock:errorBlock];

    manager.newsLastUpdated = [[NSDate date] timeIntervalSince1970];
}

+(void)getPopularAsyncFromView:(id)view
                     listCount:(NSUInteger)listCount
                      listSize:(NSUInteger)listSize
                   resultBlock:(void (^)(NSArray *))resultBlock
                    errorBlock:(void (^)(int, NSString *))errorBlock
{
    BARPCManager* manager = [BARPCManager instance];
    NSArray *params = @[manager.libraryApiKey, manager.libraryId, manager.language, [NSNumber numberWithUnsignedInteger:listCount], [NSNumber numberWithUnsignedInteger:listSize]];
    [self performRPCMethod:@"library.getPopular" args:params requestId:BA_REQID_GET_POPULAR view:view resultBlock:resultBlock errorBlock:errorBlock];
    
}

+(void)getObjectByBarcodeIdFromView:(id)view
                     materialnumber:(NSString *)materialnumber
                    sendHoldingInfo:(BOOL)sendHoldingInfo
                        resultBlock:(void (^)(BAFullRecord *))resultBlock
                         errorBlock:(void (^)(int, NSString *))errorBlock
{
    BARPCManager* manager = [BARPCManager instance];
    NSArray *params = @[manager.libraryApiKey, manager.libraryId, manager.language, materialnumber, [NSNumber numberWithBool:sendHoldingInfo]];
    [self performRPCMethod:@"library.getObjectByBarcodeId" args:params requestId:BA_REQID_GET_OBJECT_BY_BARCODE_ID view:view resultBlock:resultBlock errorBlock:errorBlock];
}


+(void)getLibraryNamesFromView:(id)view resultBlock:(void (^)(NSDictionary *))resultBlock errorBlock:(void (^)(int, NSString *))errorBlock
{
    BARPCManager* manager = [BARPCManager instance];
    NSArray *params = @[manager.language];
    [self performRPCMethod:@"library.getLibraryNames" args:params requestId:BA_REQID_GET_LIBRARY_NAMES view:view resultBlock:resultBlock errorBlock:errorBlock];

}

+(void)getLibrarySettingsFromView:(id)view resultBlock:(void (^)(BALibrary *))resultBlock errorBlock:(void (^)(int, NSString *))errorBlock
{
    BARPCManager* manager = [BARPCManager instance];
    NSArray *params = @[manager.libraryApiKey, manager.libraryId, manager.language];
    [self performRPCMethod:@"library.getLibrarySettings" args:params requestId:BA_REQID_GET_LIBRARY_SETTINGS view:view resultBlock:resultBlock errorBlock:errorBlock];

}



#pragma mark - General

// --- General ---

-(NSString*)getNextServerUrlString
{
    static int serverCycle = 0;
    
    NSString* cycleurl;
    
    if (self.isUsingDevServer) {
        cycleurl = [self.developerServers objectAtIndex: ((serverCycle++) % [self.developerServers count])];
    } else {
        cycleurl = [self.productionServers objectAtIndex: ((serverCycle++) % [self.productionServers count])];
    }
    return cycleurl;
}

-(NSArray*)serverUrlStrings{
    if (self.isUsingDevServer) {
        return self.developerServers;
    } else {
        return self.productionServers;
    }
}

+ (void)performRPCMethod:(NSString *)method args:(id)args requestId:(NSInteger)requestId view:(id)view resultBlock:(BARpcResultBlock)resultBlock errorBlock:(BARpcErrorBlock)errorBlock
{
    BARPCHelper* fetchHelper = [[BARPCHelper alloc] init];
    fetchHelper.resultBlock = resultBlock;
    fetchHelper.errorBlock = errorBlock;
    
    BARPCManager* manager = [BARPCManager instance];
    
    
    NSString* cycleurl = [manager getNextServerUrlString];
    
    NSArray* cookies = [[BARPCManager instance].cookiesDict objectForKey:cycleurl];
    for (NSHTTPCookie* cookie in cookies) {
        [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
    }
    
    //TODO: This will be redundant, as the sessionID will be moved to a cookie.
    if ([manager.currentPHPSessionID length]>0) {
        cycleurl = [cycleurl stringByAppendingFormat:@"?PHPSESSID=%@",manager.currentPHPSessionID];
    }
    
    DLog(@"using server url: %@",cycleurl);
    NSURL* serverURL = [NSURL URLWithString:cycleurl];
    
    
#ifdef DEBUG
    NSString* hostname = serverURL.host;
    
    const char *namebuf = [hostname UTF8String];
    struct addrinfo* addrResult;
    getaddrinfo(namebuf, "https", nil, &addrResult);
    
    while (addrResult) {
        DLog(@"got address (family %u) port %u, %u.%u.%u.%u",addrResult->ai_addr->sa_family,
             (256 *((unsigned char) addrResult->ai_addr->sa_data[0]) +
             (unsigned char) addrResult->ai_addr->sa_data[1]),
             
             (unsigned char) addrResult->ai_addr->sa_data[2],
             (unsigned char) addrResult->ai_addr->sa_data[3],
             (unsigned char) addrResult->ai_addr->sa_data[4],
             (unsigned char) addrResult->ai_addr->sa_data[5]
             );
        addrResult = addrResult->ai_next;
    }
#endif

    JSONRPCClient* client = [JSONRPCClient JSONRPCClientWithServer:serverURL delegate:fetchHelper];

    [BARPCManager addConnection:client View:view];
    
    DLog(@"calling '%@' with params %@", method, args);
    
    [client performMethod:method args:args requestId:requestId];

}

#pragma mark - Session status

+(void)updateAuthenticatedSessionTimestamp
{
    [self instance].sessionTimestamp = [[NSDate date] timeIntervalSince1970];
}

+(void)invalidateAuthenticatedSessionTimestamp
{
//    [self instance].cookies = nil;
    
    //Clear cookies for all hosts
    NSHTTPCookieStorage* cs = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    for (NSString* cycleurl in [BARPCManager instance].serverUrlStrings) {
        NSURL* serverUrlUrl = [NSURL URLWithString:cycleurl];
        
        NSArray* cookies = [cs cookiesForURL:serverUrlUrl].copy;
        
        for (NSHTTPCookie* cookie in cookies) {
            [cs deleteCookie:cookie];
        }
    }
    [BARPCManager instance].cookiesDict = nil;
    
    [self instance].currentPHPSessionID=@"";
    [self instance].currentRouteIDCookie=@"";
    [self instance].sessionTimestamp = 0;
}

+ (BOOL)isStillAuthenticated
{
    NSTimeInterval now = [[NSDate date] timeIntervalSince1970];
    return (now - [[self instance] sessionTimestamp] < BARPCMANAGER_SESSIONLIFETIME);
}

-(void)setIsUsingDevServer:(BOOL)isUsingDevServer
{
    if (isUsingDevServer != _isUsingDevServer) {
        //there is a change
        [BARPCManager invalidateAuthenticatedSessionTimestamp];
    }
    
    _isUsingDevServer = isUsingDevServer;
}

@end
